Package com.mxgraph.swing.handler

Source Code of com.mxgraph.swing.handler.mxConnectionHandler

/**
* $Id: mxConnectionHandler.java,v 1.25 2010-08-23 15:27:16 gaudenz Exp $
* Copyright (c) 2008, Gaudenz Alder
*/
package com.mxgraph.swing.handler;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.mxGraphComponent.mxGraphControl;
import com.mxgraph.swing.util.mxMouseAdapter;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;

/**
* Connection handler creates new connections between cells. This control is used to display the connector
* icon, while the preview is used to draw the line.
*/
public class mxConnectionHandler extends mxMouseAdapter
{

  /**
   *
   */
  private static final long serialVersionUID = -2543899557644889853L;

  /**
   *
   */
  public static Cursor CONNECT_CURSOR = new Cursor(Cursor.HAND_CURSOR);

  /**
   *
   */
  protected mxGraphComponent graphComponent;

  /**
   *
   */
  protected mxConnectPreview connectPreview;

  /**
   * Specifies the icon to be used for creating new connections. If this is
   * specified then it is used instead of the handle. Default is null.
   */
  protected ImageIcon connectIcon = null;

  /**
   * Specifies the size of the handle to be used for creating new
   * connections. Default is mxConstants.CONNECT_HANDLE_SIZE.
   */
  protected int handleSize = mxConstants.CONNECT_HANDLE_SIZE;

  /**
   * Specifies if a handle should be used for creating new connections. This
   * is only used if no connectIcon is specified. If this is false, then the
   * source cell will be highlighted when the mouse is over the hotspot given
   * in the marker. Default is mxConstants.CONNECT_HANDLE_ENABLED.
   */
  protected boolean handleEnabled = mxConstants.CONNECT_HANDLE_ENABLED;

  /**
   *
   */
  protected boolean select = true;

  /**
   * Specifies if the source should be cloned and used as a target if no
   * target was selected. Default is false.
   */
  protected boolean createTarget = false;

  /**
   * Appearance and event handling order wrt subhandles.
   */
  protected boolean keepOnTop = true;

  /**
   *
   */
  protected transient Point first;

  /**
   *
   */
  protected transient Rectangle bounds;

  /**
   *
   */
  protected transient mxCellState source;

  /**
   *
   */
  protected transient mxCellMarker marker;

  /**
   *
   */
  protected transient String error;

  /**
   *
   */
  protected boolean enabled = true;

  /**
   *
   */
  protected transient mxIEventListener resetHandler = new mxIEventListener()
  {
    public void invoke(Object source, mxEventObject evt)
    {
      reset();
    }
  };

  /**
   *
   * @param graphComponent
   */
  public mxConnectionHandler(mxGraphComponent graphComponent)
  {
    this.graphComponent = graphComponent;

    // Installs the paint handler
    graphComponent.addListener(mxEvent.AFTER_PAINT, new mxIEventListener()
    {
      public void invoke(Object sender, mxEventObject evt)
      {
        Graphics g = (Graphics) evt.getProperty("g");
        paint(g);
      }
    });

    connectPreview = createConnectPreview();

    mxGraphControl graphControl = graphComponent.getGraphControl();
    graphControl.addMouseListener(this);
    graphControl.addMouseMotionListener(this);

    mxGraphView view = graphComponent.getGraph().getView();
    view.addListener(mxEvent.SCALE, resetHandler);
    view.addListener(mxEvent.TRANSLATE, resetHandler);
    view.addListener(mxEvent.SCALE_AND_TRANSLATE, resetHandler);

    mxIGraphModel model = graphComponent.getGraph().getModel();
    model.addListener(mxEvent.CHANGE, resetHandler);

    marker = new mxCellMarker(graphComponent)
    {
      /**
       *
       */
      private static final long serialVersionUID = 103433247310526381L;

      // Overrides to return cell at location only if valid (so that
      // there is no highlight for invalid cells that have no error
      // message when the mouse is released)
      protected Object getCell(MouseEvent e)
      {
        Object cell = super.getCell(e);

        if (isConnecting())
        {
          if (source != null)
          {
            error = validateConnection(source.getCell(), cell);

            if (error != null && error.length() == 0)
            {
              cell = null;

              // Enables create target inside groups
              if (createTarget)
              {
                error = null;
              }
            }
          }
        }
        else if (!isValidSource(cell))
        {
          cell = null;
        }

        return cell;
      }

      // Sets the highlight color according to isValidConnection
      protected boolean isValidState(mxCellState state)
      {
        if (isConnecting())
        {
          return error == null;
        }
        else
        {
          return super.isValidState(state);
        }
      }

      // Overrides to use marker color only in highlight mode or for
      // target selection
      protected Color getMarkerColor(MouseEvent e, mxCellState state,
          boolean isValid)
      {
        return (isHighlighting() || isConnecting()) ? super
            .getMarkerColor(e, state, isValid) : null;
      }

      // Overrides to use hotspot only for source selection otherwise
      // intersects always returns true when over a cell
      protected boolean intersects(mxCellState state, MouseEvent e)
      {
        if (!isHighlighting() || isConnecting())
        {
          return true;
        }

        return super.intersects(state, e);
      }
    };

    marker.setHotspotEnabled(true);
  }

  /**
   *
   */
  protected mxConnectPreview createConnectPreview()
  {
    return new mxConnectPreview(graphComponent);
  }

  /**
   *
   */
  public mxConnectPreview getConnectPreview()
  {
    return connectPreview;
  }
 
  /**
   *
   */
  public void setConnectPreview(mxConnectPreview value)
  {
    connectPreview = value;
  }

  /**
   * Returns true if the source terminal has been clicked and a new
   * connection is currently being previewed.
   */
  public boolean isConnecting()
  {
    return connectPreview.isActive();
  }

  /**
   * Returns true if no connectIcon is specified and handleEnabled is false.
   */
  public boolean isHighlighting()
  {
    return connectIcon == null && !handleEnabled;
  }

  /**
   *
   */
  public boolean isEnabled()
  {
    return enabled;
  }

  /**
   *
   */
  public void setEnabled(boolean value)
  {
    enabled = value;
  }

  /**
   *
   */
  public boolean isKeepOnTop()
  {
    return keepOnTop;
  }

  /**
   *
   */
  public void setKeepOnTop(boolean value)
  {
    keepOnTop = value;
  }

  /**
   *
   */
  public void setConnectIcon(ImageIcon value)
  {
    connectIcon = value;
  }

  /**
   *
   */
  public ImageIcon getConnecIcon()
  {
    return connectIcon;
  }

  /**
   *
   */
  public void setHandleEnabled(boolean value)
  {
    handleEnabled = value;
  }

  /**
   *
   */
  public boolean isHandleEnabled()
  {
    return handleEnabled;
  }

  /**
   *
   */
  public void setHandleSize(int value)
  {
    handleSize = value;
  }

  /**
   *
   */
  public int getHandleSize()
  {
    return handleSize;
  }

  /**
   *
   */
  public mxCellMarker getMarker()
  {
    return marker;
  }

  /**
   *
   */
  public void setMarker(mxCellMarker value)
  {
    marker = value;
  }

  /**
   *
   */
  public void setCreateTarget(boolean value)
  {
    createTarget = value;
  }

  /**
   *
   */
  public boolean isCreateTarget()
  {
    return createTarget;
  }

  /**
   *
   */
  public void setSelect(boolean value)
  {
    select = value;
  }

  /**
   *
   */
  public boolean isSelect()
  {
    return select;
  }

  /**
   *
   */
  public void reset()
  {
    connectPreview.stop(false);
    setBounds(null);
    marker.reset();
    source = null;
    first = null;
    error = null;
  }

  /**
   *
   */
  public Object createTargetVertex(MouseEvent e, Object source)
  {
    mxGraph graph = graphComponent.getGraph();
    Object clone = graph.cloneCells(new Object[] { source })[0];
    mxIGraphModel model = graph.getModel();
    mxGeometry geo = model.getGeometry(clone);

    if (geo != null)
    {
      mxPoint point = graphComponent.getPointForEvent(e);
      geo.setX(graph.snap(point.getX() - geo.getWidth() / 2));
      geo.setY(graph.snap(point.getY() - geo.getHeight() / 2));
    }

    return clone;
  }

  /**
   *
   */
  public boolean isValidSource(Object cell)
  {
    return graphComponent.getGraph().isValidSource(cell);
  }

  /**
   * Returns true. The call to mxGraph.isValidTarget is implicit by calling
   * mxGraph.getEdgeValidationError in validateConnection. This is an
   * additional hook for disabling certain targets in this specific handler.
   */
  public boolean isValidTarget(Object cell)
  {
    return true;
  }

  /**
   * Returns the error message or an empty string if the connection for the
   * given source target pair is not valid. Otherwise it returns null.
   */
  public String validateConnection(Object source, Object target)
  {
    if (target == null && createTarget)
    {
      return null;
    }

    if (!isValidTarget(target))
    {
      return "";
    }

    return graphComponent.getGraph().getEdgeValidationError(
        connectPreview.getPreviewState().getCell(), source, target);
  }

  /**
   *
   */
  public void mousePressed(MouseEvent e)
  {
    if (!graphComponent.isForceMarqueeEvent(e)
        && !graphComponent.isPanningEvent(e)
        && !e.isPopupTrigger()
        && graphComponent.isEnabled()
        && isEnabled()
        && !e.isConsumed()
        && ((isHighlighting() && marker.hasValidState()) || (!isHighlighting()
            && bounds != null && bounds.contains(e.getPoint()))))
    {
      start(e, marker.getValidState());
      e.consume();
    }
  }

  /**
   *
   */
  public void start(MouseEvent e, mxCellState state)
  {
    first = e.getPoint();
    connectPreview.start(e, state, "");
  }

  /**
   *
   */
  public void mouseMoved(MouseEvent e)
  {
    mouseDragged(e);

    if (isHighlighting() && !marker.hasValidState())
    {
      source = null;
    }

    if (!isHighlighting() && source != null)
    {
      int imgWidth = handleSize;
      int imgHeight = handleSize;

      if (connectIcon != null)
      {
        imgWidth = connectIcon.getIconWidth();
        imgHeight = connectIcon.getIconHeight();
      }

      int x = (int) source.getCenterX() - imgWidth / 2;
      int y = (int) source.getCenterY() - imgHeight / 2;

      if (graphComponent.getGraph().isSwimlane(source.getCell()))
      {
        mxRectangle size = graphComponent.getGraph().getStartSize(
            source.getCell());

        if (size.getWidth() > 0)
        {
          x = (int) (source.getX() + size.getWidth() / 2 - imgWidth / 2);
        }
        else
        {
          y = (int) (source.getY() + size.getHeight() / 2 - imgHeight / 2);
        }
      }

      setBounds(new Rectangle(x, y, imgWidth, imgHeight));
    }
    else
    {
      setBounds(null);
    }

    if (source != null && (bounds == null || bounds.contains(e.getPoint())))
    {
      graphComponent.getGraphControl().setCursor(CONNECT_CURSOR);
      e.consume();
    }
  }

  /**
   *
   */
  public void mouseDragged(MouseEvent e)
  {
    if (!e.isConsumed() && graphComponent.isEnabled() && isEnabled()
        && (e.getButton() == 0 || connectPreview.isActive()))
    {
      mxCellState state = marker.process(e);

      if (connectPreview.isActive())
      {
        mxGraph graph = graphComponent.getGraph();
        double x = graph.snap(e.getX());
        double y = graph.snap(e.getY());

        connectPreview.update(e, marker.getValidState(), x, y);
        setBounds(null);
        e.consume();
      }
      else
      {
        source = state;
      }
    }
  }

  /**
   *
   */
  public void mouseReleased(MouseEvent e)
  {
    if (error != null)
    {
      if (error.length() > 0)
      {
        JOptionPane.showMessageDialog(graphComponent, error);
      }
    }
    else if (first != null)
    {
      mxGraph graph = graphComponent.getGraph();
      double dx = first.getX() - e.getX();
      double dy = first.getY() - e.getY();

      if (connectPreview.isActive()
          && (marker.hasValidState() || isCreateTarget() || graph
              .isAllowDanglingEdges()))
      {
        graph.getModel().beginUpdate();

        try
        {
          if (!marker.hasValidState() && isCreateTarget())
          {
            Object vertex = createTargetVertex(e, source.getCell());
            Object dropTarget = graph.getDropTarget(
                new Object[] { vertex }, e.getPoint(),
                graphComponent.getCellAt(e.getX(), e.getY()));

            if (vertex != null)
            {
              // Disables edges as drop targets if the target cell was created
              if (dropTarget == null
                  || !graph.getModel().isEdge(dropTarget))
              {
                mxCellState pstate = graph.getView().getState(
                    dropTarget);

                if (pstate != null)
                {
                  mxGeometry geo = graph.getModel()
                      .getGeometry(vertex);

                  mxPoint origin = pstate.getOrigin();
                  geo.setX(geo.getX() - origin.getX());
                  geo.setY(geo.getY() - origin.getY());
                }
              }
              else
              {
                dropTarget = graph.getDefaultParent();
              }

              graph.addCells(new Object[] { vertex }, dropTarget);
            }

            double x = graph.snap(e.getX());
            double y = graph.snap(e.getY());

            // FIXME: Here we pre-create the state for the vertex to be
            // inserted in order to invoke update in the connectPreview.
            // This means we have a cell state which should be created
            // after the model.update, so this should be fixed.
            mxCellState targetState = graph.getView().getState(
                vertex, true);
            connectPreview.update(e, targetState, x, y);
          }

          Object cell = connectPreview.stop(graphComponent
              .isSignificant(dx, dy), e);

          if (cell != null)
          {
            graphComponent.getGraph().setSelectionCell(cell);
          }

          e.consume();
        }
        finally
        {
          graph.getModel().endUpdate();
        }
      }
    }

    reset();
  }

  /**
   *
   */
  public void setBounds(Rectangle value)
  {
    if ((bounds == null && value != null)
        || (bounds != null && value == null)
        || (bounds != null && value != null && !bounds.equals(value)))
    {
      Rectangle tmp = bounds;

      if (tmp != null)
      {
        if (value != null)
        {
          tmp.add(value);
        }
      }
      else
      {
        tmp = value;
      }

      bounds = value;

      if (tmp != null)
      {
        graphComponent.getGraphControl().repaint(tmp);
      }
    }
  }

  /**
   *
   */
  public void paint(Graphics g)
  {
    if (bounds != null)
    {
      if (connectIcon != null)
      {
        g.drawImage(connectIcon.getImage(), bounds.x, bounds.y,
            bounds.width, bounds.height, null);
      }
      else if (handleEnabled)
      {
        g.setColor(Color.BLACK);
        g.draw3DRect(bounds.x, bounds.y, bounds.width - 1,
            bounds.height - 1, true);
        g.setColor(Color.GREEN);
        g.fill3DRect(bounds.x + 1, bounds.y + 1, bounds.width - 2,
            bounds.height - 2, true);
        g.setColor(Color.BLUE);
        g.drawRect(bounds.x + bounds.width / 2 - 1, bounds.y
            + bounds.height / 2 - 1, 1, 1);
      }
    }
  }
}
TOP

Related Classes of com.mxgraph.swing.handler.mxConnectionHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.